package cn.flying.base.process.service;
import java.util.Date;

import javax.annotation.Resource;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.Job;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;

import cn.flying.base.common.enums.RtCodeEnum;
import cn.flying.base.common.exception.BizException;
import cn.flying.base.common.utils.StringUtils;
import cn.flying.base.common.vo.PageRequest;
import cn.flying.base.common.vo.PageResponse;
import cn.flying.base.process.bo.ProcException;
import cn.flying.base.process.bo.ProcInstance;
import cn.flying.base.process.enums.BizProcDefTypeEnum;
import cn.flying.base.process.mapper.ProcessBizConfigMapper;
import cn.flying.base.process.mapper.ProcessExceptionMapper;
import cn.flying.base.process.model.CfProcBizConfig;

/**
 * @description: 工作流处理服务
 * @author: lvyong
 * @date: 2022年12月17日 20:47
 * @version: 1.0
 */
@Service
public class ProcessManageService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private ProcessEngine processEngine;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private IdentityService identityService;
    @Resource
    private ManagementService managementService;
    @Resource
    private TaskService taskService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private HistoryService historyService;
    @Resource
    private ProcessBizConfigMapper processBizConfigMapper;
    @Resource
    private ProcessExceptionMapper processExceptionMapper;


    public ProcessEngine getProcessEngine() {
        return processEngine;
    }


    public RepositoryService getRepositoryService() {
        return repositoryService;
    }


    public IdentityService getIdentityService() {
        return identityService;
    }


    public ManagementService getManagementService() {
        return managementService;
    }


    public TaskService getTaskService() {
        return taskService;
    }


    public RuntimeService getRuntimeService() {
        return runtimeService;
    }


    public HistoryService getHistoryService() {
        return historyService;
    }


    /**
     * 启动流程
     *
     * @param bizKey
     * @param typeEnum
     * @param variables
     * @param tenantId
     * @return
     */
    public String startProcessByDefKey(String bizKey, BizProcDefTypeEnum typeEnum, Map<String, Object> variables, String tenantId) {
        CfProcBizConfig bizConfig = processBizConfigMapper.getProcBizConfig(typeEnum.getCode());
        if (bizConfig == null) {
            throw new BizException(RtCodeEnum.R90000.getCode(), "没有查询到【" + typeEnum.getCode() + "】的流程配置");
        }
        ProcessInstance processInstance;
        if (StringUtils.isNotBlank(tenantId)) {
            processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(bizConfig.getProcDefKey(), bizKey, variables, tenantId);
        } else {
            processInstance = runtimeService.startProcessInstanceByKey(bizConfig.getProcDefKey(), bizKey, variables);
        }
        logger.info("启动流程成功！流程实例ID：【{}】，租户：【】，业务流水号：【{}】", processInstance.getProcessInstanceId(), tenantId, bizKey);
        return processInstance.getProcessInstanceId();
    }

    /**
     * 流程实例列表查询
     *
     * @param pageRequest
     * @return
     */
    public PageResponse getProcessInstPageList(PageRequest<ProcInstance> pageRequest) {
        Integer pageNum = pageRequest.getPageNum();
        Integer pageSize = pageRequest.getPageSize();
        ProcInstance instance = pageRequest.getParam();
        ProcessInstanceQuery instanceQuery = runtimeService.createProcessInstanceQuery();
        if (StringUtils.isNotBlank(instance.getBizKey())) {
            instanceQuery.processInstanceBusinessKey(instance.getBizKey());
        }
        if (StringUtils.isNotBlank(instance.getName())) {
            instanceQuery.processInstanceName(instance.getName());
        }
        List<ProcessInstance> instances = instanceQuery.listPage((pageNum - 1) * pageSize, pageSize);

        List<ProcInstance> list = instances.stream().map(inst -> {
            ProcInstance procInstance = new ProcInstance();
            procInstance.setProcDefId(inst.getProcessDefinitionId());
            procInstance.setProcDefName(inst.getProcessDefinitionName());
            procInstance.setProcDefKey(inst.getProcessDefinitionKey());
            procInstance.setProcDefVersion(inst.getProcessDefinitionVersion());
            procInstance.setBizKey(inst.getBusinessKey());
            procInstance.setTenantId(inst.getTenantId());
            procInstance.setName(inst.getName());
            procInstance.setStartTime(inst.getStartTime());
            return procInstance;
        }).collect(Collectors.toList());

        return new PageResponse((long) list.size(), pageNum, list);
    }

    /**
     * 查询未分配代理人的任务（未领取）
     *
     * @param pageRequest
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public PageResponse getUnClaimTaskPageList(PageRequest<String> pageRequest) {
        String tenantId = pageRequest.getParam();
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (StringUtils.isNotBlank(tenantId)) {
            taskQuery.taskTenantId(tenantId);
        }
        List<Task> list = taskQuery.taskUnassigned().taskTenantId(tenantId).listPage((pageRequest.getPageNum() - 1) * pageRequest.getPageSize(), pageRequest.getPageSize());
        return new PageResponse((long) list.size(), pageRequest.getPageNum(), list);
    }

    /**
     * 领取任务
     *
     * @param taskId
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    public void claimTask(String taskId, String userId) {
        taskService.claim(taskId, userId);
        logger.info("用户【{}】领取任务【{}】成功！", userId, taskId);
    }

    /**
     * 查询我的待办任务
     *
     * @param pageRequest
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public PageResponse getUnCompleteTasksPageList(PageRequest<Map<String, String>> pageRequest) {
        Map<String, String> param = pageRequest.getParam();
        String assignee = param.get("assignee");
        String tenantId = param.get("tenantId");
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (StringUtils.isNotBlank(tenantId)) {
            taskQuery.taskTenantId(tenantId);
        }
        List<Task> list = taskQuery.taskAssignee(assignee).taskTenantId(tenantId).listPage((pageRequest.getPageNum() - 1) * pageRequest.getPageSize(), pageRequest.getPageSize());
        return new PageResponse((long) list.size(), pageRequest.getPageNum(), list);
    }

    /**
     * 完成提交任务
     *
     * @param taskId
     * @param variables
     */
    @Transactional(rollbackFor = Exception.class)
    public void completeTask(String taskId, Map<String, Object> variables) {
        taskService.complete(taskId, variables);
        logger.info("任务【{}】提交成功！", taskId);
    }

    /**
     * 触发任务（针对Receive Task）
     *
     * @param bizKey
     * @param taskId
     */
    @Transactional(rollbackFor = Exception.class)
    public void triggerTask(String bizKey, String taskId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(bizKey).singleResult();
        Execution execution = runtimeService.createExecutionQuery().processInstanceId(processInstance.getId()).activityId(taskId).singleResult();
        runtimeService.trigger(execution.getId());
        logger.info("成功触发任务【】执行！", taskId);
    }

    /**
     * 为任务添加用户组
     *
     * @param taskId
     * @param groupId
     */
    @Transactional(rollbackFor = Exception.class)
    public void addCandidateGroup(String taskId, String groupId) {
        taskService.addCandidateGroup(taskId, groupId);
    }

    /**
     * 查询所有的作业异常信息
     *
     * @param pageRequest
     * @return
     */
    public PageResponse getAllException(PageRequest<String> pageRequest) {
        Integer pageNum = pageRequest.getPageNum();
        Integer pageSize = pageRequest.getPageSize();
        String bizKey = pageRequest.getParam();
        PageHelper.offsetPage((pageNum - 1) * pageSize, pageSize);
        Page<ProcException> page = (Page<ProcException>) processExceptionMapper.getAllException(bizKey);
        return new PageResponse<>(page.getTotal(), pageNum, page.getResult());
    }

    /**
     * 查询作业异常堆栈信息
     *
     * @param jobId
     * @return
     */
    public String getExceptionTrace(String jobId) {
        String stacktrace = managementService.getDeadLetterJobExceptionStacktrace(jobId);
        return stacktrace;
    }

    /**
     * 处理异常
     *
     * @param executionId
     */
    public void dealException(String executionId) {
        Job job = managementService.createDeadLetterJobQuery().withException().executionId(executionId).singleResult();
        //加入执行，重试3次
        managementService.moveDeadLetterJobToExecutableJob(job.getId(), 3);
    }

    /**
     * 取消删除流程
     *
     * @param bizKey
     * @param reason
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelProcess(String bizKey, String reason) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(bizKey).singleResult();
        runtimeService.deleteProcessInstance(processInstance.getProcessInstanceId(), reason);
    }

    public void triggerTask(String assignee) {
        List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery().taskAssignee(assignee).orderByTaskCreateTime().desc().list();

    }

}
